/*
 * Copyright (C) 2009 M. Hautle.
 * 
 * This file is part of TrackedBean.
 * 
 * TrackedBean is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * TrackedBean is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with TrackedBean. If not, see <http://www.gnu.org/licenses/>.
 */
package ch.trackedbean.binding.internal.mapping;

import java.awt.*;
import java.util.List;
import javax.swing.*;
import javax.swing.text.*;
import org.jdesktop.beansbinding.*;
import org.jdesktop.swingbinding.*;
import ch.trackedbean.binding.mapping.*;
import ch.trackedbean.tracking.*;
import ch.trackedbean.tracking.internal.*;

/**
 * Util class for mapping creation.
 * 
 * @author M. Hautle
 */
public final class MappingUtils {
    /** Property for {@link TextComponent}s. */
    private static final BeanProperty<JTextComponent, String> TEXT_COMPONENT_PROP = BeanProperty.create("text");

    /** Property for {@link JLabel}s. */
    private static final BeanProperty<JLabel, String> LABEL_PROP = BeanProperty.create("text");

    /** Property for {@link JComboBox}es. */
    private static final BeanProperty<JComboBox, String> COMBOBOX_PROP = BeanProperty.create("selectedItem");

    /** Property for {@link IDependentElement}s. */
    private static final BeanProperty<IDependentElement, Object> DEPENDENT_ELEMENT_PROP = BeanProperty.create("dependentValue");

    /** Property for {@link IStatusDependent}. */
    private static final BeanProperty<IStatusDependent, Status> STATUS_DEPENDENT_PROP = BeanProperty.create("status");

    /** Property for the selection of a {@link JList}. */
    private static final BeanProperty<JList, List<?>> LIST_SELECTION_PROP = BeanProperty.create("selectedElements_IGNORE_ADJUSTING");

    /**
     * Private constructor to avoid instantiation
     */
    private MappingUtils() {
    }

    /**
     * Creates a binding between two beans
     * 
     * @param <SS> The source bean type
     * @param <SV> The source property type
     * @param <TS> The destination bean type
     * @param <TV> The destination property type
     * @param src The source bean
     * @param srcPath The source path
     * @param dst The destination bean
     * @param dstPath The destination path
     * @param readOnly True if the binding is just read only
     * @return The binding for the given beans
     */
    public static <SS, SV, TS, TV> Binding<SS, SV, TS, TV> createBinding(final SS src, final String srcPath, final TS dst, final String dstPath,
            final boolean readOnly) {
        return Bindings.createAutoBinding(readOnly ? AutoBinding.UpdateStrategy.READ : AutoBinding.UpdateStrategy.READ_WRITE, src,
                BeanProperty.<SS, SV> create(srcPath), dst, BeanProperty.<TS, TV> create(dstPath));
    }

    /**
     * Creates a binding between the given bean and the given field.
     * 
     * @param <SO> The type of the bean
     * @param <ST> The type of the bean property
     * @param model The bean to link
     * @param path The path to the bean property
     * @param field The {@link JTextComponent} to link
     * @param readOnly True if the binding is just read only
     * @return The binding of the bean and the textcomponent
     */
    public static <SO, ST> Binding<SO, ST, JTextComponent, String> createBinding(final SO model, final String path, final JTextComponent field,
            final boolean readOnly) {
        return Bindings.createAutoBinding(readOnly ? AutoBinding.UpdateStrategy.READ : AutoBinding.UpdateStrategy.READ_WRITE, model,
                BeanProperty.<SO, ST> create(path), field, TEXT_COMPONENT_PROP);
    }

    /**
     * Creates a binding between the given bean and the given combobox.
     * 
     * @param <SO> The type of the bean
     * @param <ST> The type of the bean property
     * @param model The bean to link
     * @param path The path to the bean property
     * @param combobox The {@link JComboBox} to link
     * @param readOnly True if the binding is just read only
     * @return The binding of the bean and the combobox
     */
    public static <SO, ST> Binding<SO, ST, JComboBox, String> createBinding(final SO model, final String path, final JComboBox combobox, final boolean readOnly) {
        return Bindings.createAutoBinding(readOnly ? AutoBinding.UpdateStrategy.READ : AutoBinding.UpdateStrategy.READ_WRITE, model,
                BeanProperty.<SO, ST> create(path), combobox, COMBOBOX_PROP);
    }

    /**
     * Creates a binding between the given bean property and the given list.
     * 
     * @param <SO> The type of the bean
     * @param <T> The content type of the list
     * @param model The bean to link
     * @param path The path to the bean property containing the list
     * @param list The {@link JList} to link
     * @param detailBinding The EL expression (relative to a list element) to use for the cell renderer or null
     * @return The binding of the bean and the list
     */
    public static <SO, T> JListBinding<T, SO, JList> createBinding(final SO model, final String path, final JList list, String detailBinding) {
        final JListBinding<T, SO, JList> b = SwingBindings.createJListBinding(AutoBinding.UpdateStrategy.READ, model, BeanProperty.<SO, List<T>> create(path),
                list, (String) null);
        if (detailBinding != null)
            b.setDetailBinding(ELProperty.<T, String> create(detailBinding));
        return b;
    }

    /**
     * Creates a binding between the given bean property and the selection of the given {@link JList}.
     * 
     * @param <SO> The type of the bean
     * @param <T> The content type of the list
     * @param model The bean to link
     * @param path The path to the bean property containing the list
     * @param list The {@link JList} to link
     * @return The binding of the bean property and the {@link JList} selection
     */
    public static <SO, T> AutoBinding<JList, List<?>, SO, T> createSelectionBinding(final SO model, final String path, final JList list) {
        return Bindings.createAutoBinding(AutoBinding.UpdateStrategy.READ_WRITE, list, LIST_SELECTION_PROP, model, BeanProperty.<SO, T> create(path));
    }

    /**
     * Creates a binding between the given bean and the given label.
     * 
     * @param <SO> The type of the bean
     * @param <ST> The type of the bean property
     * @param model The bean to link
     * @param path The path to the bean property
     * @param label The {@link JLabel} to link
     * @return The binding of the bean and the label
     */
    public static <SO, ST> Binding<SO, ST, JLabel, String> createBinding(final SO model, final String path, final JLabel label) {
        return Bindings.createAutoBinding(AutoBinding.UpdateStrategy.READ, model, BeanProperty.<SO, ST> create(path), label, LABEL_PROP);
    }

    /**
     * Creates a binding between the status of the source property and the destination property
     * 
     * @param <SS> The source bean type
     * @param <TS> The destination bean type
     * @param <TV> The destination property type
     * @param src The source bean
     * @param srcPath The source path
     * @param dst The destination bean
     * @param dstPath The destination path
     * @return The binding for the given beans
     */
    public static <SS, TS, TV> Binding<SS, Status, TS, TV> createStatusBinding(final SS src, final String srcPath, final TS dst, final String dstPath) {
        return Bindings
                .createAutoBinding(AutoBinding.UpdateStrategy.READ, src, StatusProperty.<SS> create(srcPath), dst, BeanProperty.<TS, TV> create(dstPath));
    }

    /**
     * Binds the given {@link IStatusDependent} to the specified property.
     * 
     * @param <SS> The source type
     * @param <TV> The destination property type
     * @param src The source object
     * @param path The path to the bean property (may also be {@link IBeanStatus#BEAN_STATUS} or {@link IBeanStatus#BEAN_STRUCTURE_STATUS})
     * @param element The element to bind
     * @return The binding
     */
    public static <SS, TV> AutoBinding<SS, Status, IStatusDependent, Status> createStatusDependentBinding(final SS src, final String path,
            final IStatusDependent element) {
        return Bindings.createAutoBinding(AutoBinding.UpdateStrategy.READ, src, StatusProperty.<SS> create(path), element, STATUS_DEPENDENT_PROP);
    }

    /**
     * Binds the given {@link IDependentElement} to the specified property.
     * 
     * @param <SS> The source type
     * @param <TV> The destination property type
     * @param src The source object
     * @param path The path to the bean property
     * @param element The element to bind
     * @return The binding
     */
    public static <SS, TV> AutoBinding<SS, TV, IDependentElement, Object> createDependentElementBinding(final SS src, final String path,
            final IDependentElement element) {
        return Bindings.createAutoBinding(AutoBinding.UpdateStrategy.READ, src, BeanProperty.<SS, TV> create(path), element, DEPENDENT_ELEMENT_PROP);
    }
}
